<?php
class MyRedis{
    
    const TIMEOUT_CONN = 0.5;
    const CONN_EXPIRE = 30; //30s

    private $_conf;
    static $rds_array = array();
    
    public function __construct()
    {
        $this->_conf = get_instance()->config->item('redis_default');

    }

    private function connect($method, $k)
    {/*{{{*/
        //$idx = $this->map($k, count($this->_conf));
        $conf = $this->getcluster($method, $this->_conf);
        $key = md5(serialize($conf));
        if( isset(self::$rds_array[$key]) && self::$rds_array[$key]['obj'] instanceof Redis && (time() - self::$rds_array[$key]['t']) < self::CONN_EXPIRE)  
        {
            return self::$rds_array[$key]['obj'];
        }
        
        $timeout = isset($conf['timeout']) ? intval($conf['timeout']) : self::TIMEOUT_CONN;
        $rds = new Redis();
        $rds->connect($conf['host'], $conf['port'], $timeout);
        if(isset($conf['password'])) $rds->auth($conf['user'] . ":" . $conf['password']);
        self::$rds_array[$key]['obj'] = $rds;
        self::$rds_array[$key]['t'] = time();
        return $rds;
    }/*}}}*/

    private function map($k, $cnt) 
    {/*{{{*/
        $idx = 0;
        if ($cnt > 1)
        {
            $hashcode = (crc32($k) >> 16) & 0x7fff;
            $idx = $hashcode % $cnt;
        }
        return $idx;
    }/*}}}*/

    private function getcluster($method, $conf)
    {/*{{{*/
        $cluster = 'master';
        $method = ",{$method},";
        foreach($this->getSlaveMethods() as $v)
        {
            $v = ",{$v},";
            if(strpos($v, $method) !== false)
            {
                $cluster = 'slave';
                break;
            }
        }
        return $conf[$cluster];
    }/*}}}*/

    private function getSlaveMethods()
    {/*{{{*/
        return array(
        'keys'  => 'dump,exists,keys,pttl,randomkey,ttl,type',
        'string'=> 'bitcount,get,getbit,getrange,mget,strlen',
        'hashs' => 'hexists,hget,hgetall,hkeys,hlen,hmget,hvals',
        'lists' => 'lindex,llen,lrange',
        'sets'  => 'scard,sdiff,sinter,sismember,smembers,srandmember',
        );
    }/*}}}*/
    
    public function __call($method, $args)
    {/*{{{*/
        if(empty($this->_conf['master']) || empty($this->_conf['slave']))
        {
            if(!isset($this->_conf['host'])) $this->_conf = $this->_conf[0];
            $conf['master'] = $this->_conf;
            $conf['slave'] = $this->_conf;
            $this->_conf = $conf;
        }
        $rds = $this->connect($method, $args[0]);
        if (!$rds) return false;
        
        $r = new \ReflectionObject($rds);
        $re = $r->getMethod($method)->invokeArgs($rds, $args);
        return $re;
    }/*}}}*/

    public function clean()
    {/*{{{*/
        foreach (self::$rds_array as $k=>$rds)
        {
            $rds['obj']->close();
        }
        self::$rds_array = array();
    }/*}}}*/
}
